﻿using DotNetCore.CAP;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MT.Enterprise.Utils.Extensions;
using Newtonsoft.Json;
using Nito.AsyncEx.Synchronous;
using NP.BPMReportPlatform.DBHelper;
using NP.BPMReportPlatform.EmailHelper;
using NP.BPMReportPlatform.Entities.BPM_Business;
using NP.BPMReportPlatform.Entities.BPM_Business.YCLWLJGZSYC;
using NP.BPMReportPlatform.Entities.BPM_Engine;
using NP.BPMReportPlatform.Entities.BPM_Platform;
using NP.BPMReportPlatform.Entities.BPM_Process;
using NP.BPMReportPlatform.RecurringJobs.Dto;
using NP.BPMReportPlatform.RecurringJobs.Dto.YCLWLJGZSYC;
using NP.BPMReportPlatform.Utilities;
using NP.BPMReportPlatform.YCLWLJGZSYC.Dto;
using NP.Enterprise.Utils.Http;
using Serilog;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NP.BPMReportPlatform.RecurringJobs
{
    /// <summary>
    /// 每月4号发送邮件提醒未提交和未审批流程
    /// </summary>
    public class YCLWLJGZSYCRecurringJob : BpmReportPlatformRecurringJob, IYCLWLJGZSYCRecurringJob
    {
        private readonly SqlSugarClient _db, _dbPlatform, _dbEngine, _dbProcess;
        private readonly INPRestHttpHelper _nPRestHttp;
        private readonly IConfiguration _configuration;
        private readonly ILogger _logger;
        private readonly IEmailAPPService _emailAPPService;
        private readonly ICapPublisher _capBus;
        private IHostingEnvironment _env;
        private readonly string TestEmail = "yangxin@movit-tech.com";
        private readonly string TestCCEmail = "yangxin@movit-tech.com";

        public YCLWLJGZSYCRecurringJob(
            ISqlSugarAppService sqlSugarAppService, 
            IConfiguration configuration,
            INPRestHttpHelper nPRestHttp,
            ILogger logger,
            IEmailAPPService emailAPPService,
            ICapPublisher capBus,
            IHostingEnvironment env)
        {
            _db = sqlSugarAppService.GetBusinessInstance();
            _dbPlatform = sqlSugarAppService.GetPlatformInstance();
            _dbEngine = sqlSugarAppService.GetEngineInstance();
            _dbProcess = sqlSugarAppService.GetProcessInstance();
            _nPRestHttp = nPRestHttp;
            _configuration = configuration;
            _logger = logger;
            _emailAPPService = emailAPPService;
            _capBus = capBus;
            _env = env;
        }

        /// <summary>
        /// 每个月4号，中午12:00，下午17:00，发送邮件提醒流程未提交，并CC给负责人
        /// </summary>
        /// <returns></returns>
        public async Task SendEmailForNoticeInstanceNotSubmit()
        {
            try
            {
                var PCStartUrl = await GetPCStartUrl();
                if (string.IsNullOrEmpty(PCStartUrl))
                {
                    _logger.Error("SendEmailForNoticeInstanceNotSubmit，流程未找到");
                    return;
                }
                var esbData = await VHanaPurchasePriceForecastToBpm();
                var workNumberData = esbData.Data.Select(x => x.CATEGORY_MANAGER_CODE).Distinct().ToList();
                if (!workNumberData.Any())
                {
                    _logger.Error("SendEmailForNoticeInstanceNotSubmit，ESB数据为空");
                    return;
                }
                var users = await _dbPlatform.Queryable<Users>().Where(x => workNumberData.Contains(x.WorkNumber)).ToListAsync();
                if (!users.Any())
                {
                    _logger.Error("SendEmailForNoticeInstanceNotSubmit，用户数据未找到");
                    return;
                }

                var ccEmails = GetNotSubmitCCEmails();

                var startTime = DateTime.Parse(DateTime.Now.ToString(DateFormatHelper.MouthFirstDayFormat));
                var endTime = startTime.AddMonths(1).AddSeconds(-1);

                var submitedData = await GetSubmitedData(startTime, endTime);
                var maintenanceTypes = submitedData.Select(x => x.MaintenanceType).Distinct().ToList();
                var mostMaintenanceType = 0; // 获取填写最多的维护类型
                var mostMaintenanceTypeCount = 0;
                foreach (var maintenanceType in maintenanceTypes)
                {
                    var maintenanceTypeCount = submitedData.Where(x => x.MaintenanceType == maintenanceType).Select(x => x.InstanceNumber).Distinct().Count();
                    if (maintenanceTypeCount > mostMaintenanceTypeCount)
                    {
                        mostMaintenanceTypeCount = maintenanceTypeCount;
                        mostMaintenanceType = maintenanceType;
                    }
                }

                var noticeConfig = await _db.Queryable<YCLWLJGZSYCNoticeConfig>().Where(x =>
                    x.MonthId.Equals(DateTime.Now.ToString("yyyyMMdd")) &&
                    x.IsNoticed == 0).OrderBy(x => x.AddTime, OrderByType.Desc).FirstAsync();
                var configEmails = new List<string>();
                if (noticeConfig != null)
                {
                    mostMaintenanceType = noticeConfig.MaintenanceType;
                    if (!string.IsNullOrEmpty(noticeConfig.WorkNumber))
                    {
                        var configWorkNumbers = noticeConfig.WorkNumber.Split(',');
                        configEmails = await _dbPlatform.Queryable<Users>().Where(x =>
                            configWorkNumbers.Contains(x.WorkNumber)).Select(x => x.Email).ToListAsync();
                    }
                }
                foreach (var user in users)
                {
                    var categoryInfos = esbData.Data.Where(x => x.CATEGORY_MANAGER_CODE == user.WorkNumber).ToList();
                    //获取已提交数据的事业群
                    var submitDepts = submitedData.Where(x => x.CATEGORY_MANAGER_CODE.Equals(user.WorkNumber) &&
                        (mostMaintenanceType > 0 && x.MaintenanceType == mostMaintenanceType || mostMaintenanceType == 0))
                        .Select(x => x.DEPT).Distinct().ToList();
                    //判断是否有未提交的事业群，有则推送邮件
                    if (noticeConfig != null)
                    {
                        if (categoryInfos.Any(x => noticeConfig.Dept.Split(",").Contains(x.DEPT)) &&
                            categoryInfos.Any(x => !submitDepts.Contains(x.DEPT)) &&
                            (configEmails.Any() && configEmails.Contains(user.Email) || !configEmails.Any()))
                        {
                            var deptList = categoryInfos.Where(x =>
                                !submitDepts.Contains(x.DEPT) &&
                                noticeConfig.Dept.Split(",").Contains(x.DEPT)).Select(x => x.DEPT).Distinct().ToList();
                            categoryInfos = categoryInfos.Where(x => deptList.Contains(x.DEPT)).ToList();
                            if (categoryInfos.Any())
                            {
                                await SendUnSubmitEmail(categoryInfos, user.Email, PCStartUrl, ccEmails);
                            }
                        }
                    }
                    else
                    {
                        if (categoryInfos.Any(x => !submitDepts.Contains(x.DEPT)) &&
                            (configEmails.Any() && configEmails.Contains(user.Email) || !configEmails.Any()))
                        {
                            var deptList = categoryInfos.Where(x => !submitDepts.Contains(x.DEPT)).Select(x => x.DEPT).Distinct().ToList();
                            categoryInfos = categoryInfos.Where(x => deptList.Contains(x.DEPT)).ToList();
                            if (categoryInfos.Any())
                            {
                                await SendUnSubmitEmail(categoryInfos, user.Email, PCStartUrl, ccEmails);
                            }
                        }
                    }
                }

                if (noticeConfig != null)
                {
                    noticeConfig.IsNoticed = 1;
                    await _db.Updateable(noticeConfig).ExecuteCommandAsync();
                }
            }
            catch(Exception ex)
            {
                _logger.Error($"SendEmailForNoticeInstanceNotSubmit，{ex.Message}");
            }
        }

        /// <summary>
        /// 每个月4号，下午17:00，发送邮件提醒未审批的流程
        /// </summary>
        /// <returns></returns>
        public async Task SendEmailForNoticeInstanceProcessing()
        {
            try
            {
                var startTime = DateTime.Parse(DateTime.Now.ToString(DateFormatHelper.MouthFirstDayFormat));
                var endTime = startTime.AddMonths(1).AddSeconds(-1);
                var unApproveData = await GetProcessingData(startTime, endTime);
                if (!unApproveData.Any())
                {
                    _logger.Error("SendEmailForNoticeInstanceProcessing，无待审批数据");
                    return;
                }

                var instanceDetails = await GetInstanceDetail(unApproveData.Select(x => x.InstanceNumber).ToList());
                if (!instanceDetails.Any())
                {
                    _logger.Error("SendEmailForNoticeInstanceProcessing，无待审批数据");
                    return;
                }

                var ccEmails = GetProcessingCCEmails();

                var emailUsers = await _dbPlatform.Queryable<Users>().Where(x => instanceDetails.Select(q => q.CurrentApprover).Contains(x.UserId.ToString())).ToListAsync();
                var startUsers = await _dbPlatform.Queryable<Users>().Where(x => unApproveData.Select(q => q.AddUserId).Contains(x.UserId)).ToListAsync();

                var groupInstanceDetails = instanceDetails.GroupBy(x => x.CurrentApprover);
                foreach (var groupInstanceDetail in groupInstanceDetails)
                {
                    var emailUser = emailUsers.FirstOrDefault(x => x.UserId.ToString() == groupInstanceDetail.Key);
                    if (emailUser == null)
                    {
                        continue;
                    }
                    var emailDatas = new List<YCLProcessingSendEamilDto>();
                    foreach(var groupInstance in groupInstanceDetail)
                    {
                        var unApprove = unApproveData.FirstOrDefault(x => x.InstanceNumber == groupInstance.InstanceNumber);
                        if (unApprove == null)
                        {
                            continue;
                        }
                        var startUser = startUsers.FirstOrDefault(x => x.UserId == unApprove.AddUserId);
                        if (startUser == null)
                        {
                            continue;
                        }
                        var emailData = new YCLProcessingSendEamilDto
                        {
                            InstanceNumber = groupInstance.InstanceNumber,
                            StartUser = $"{startUser.UserName}({startUser.WorkNumber})",
                            ApprovelUrl = string.Format(_configuration["BPM:ToDoURL"], groupInstance.InstanceNumber, groupInstance.TaskId),
                        };
                        emailDatas.Add(emailData);
                    }
                    if (emailDatas.Any())
                    {
                        await SendProcessingEmail(emailDatas, emailUser.Email, ccEmails);
                    }
                }
            }
            catch(Exception ex)
            {
                _logger.Error($"SendEmailForNoticeInstanceProcessing，{ex.Message}");
            }
        }

        private string GetProcessingCCEmails()
        {
            var ccEmails = "";
            if (_env.IsProduction())
            {
                var ccWorkNumbers = _db.Queryable<ApproverConfiguration>().First(x => x.BTID.Equals("YCLWLJGZSYC") && x.ApprovalNode.Equals("ccUsers") && x.Q1.Equals("ProcessingCCUsers"))?.EmployeeCode ?? "";
                if (!string.IsNullOrEmpty(ccWorkNumbers))
                {
                    var ccUsers = _dbPlatform.Queryable<Users>().Where(x => ccWorkNumbers.Split(",", StringSplitOptions.RemoveEmptyEntries).Contains(x.WorkNumber)).Select(x => x.Email).ToList();
                    ccEmails = string.Join(";", ccUsers);
                }
            }
            else
            {
                ccEmails = TestCCEmail;
            }

            return ccEmails;
        }

        private string GetNotSubmitCCEmails()
        {
            var ccEmails = "";
            if (_env.IsProduction())
            {
                var ccWorkNumbers = _db.Queryable<ApproverConfiguration>().First(x => x.BTID.Equals("YCLWLJGZSYC") && x.ApprovalNode.Equals("ccUsers") && x.Q1.Equals("NotSubmitCCUsers"))?.EmployeeCode ?? "";
                if (!string.IsNullOrEmpty(ccWorkNumbers))
                {
                    var ccUsers = _dbPlatform.Queryable<Users>().Where(x => ccWorkNumbers.Split(",", StringSplitOptions.RemoveEmptyEntries).Contains(x.WorkNumber)).Select(x => x.Email).ToList();
                    ccEmails = string.Join(";", ccUsers);
                }
            }
            else
            {
                ccEmails = TestCCEmail;
            }

            return ccEmails;
        }

        private string GetToEmails(string emails)
        {
            return _env.IsProduction() ? emails : TestEmail;
        }

        /// <summary>
        /// 获取ESB数据，所有人所有分类
        /// </summary>
        /// <returns></returns>
        private async Task<VHanaPurchasePriceForecastToBpmOuput> VHanaPurchasePriceForecastToBpm()
        {
            try
            {
                var headers = new Dictionary<string, string>
                {
                    { "Content-Type", "application/json" },
                    { "requestId", Guid.NewGuid().ToString() },
                    { "trackId", Guid.NewGuid().ToString() },
                    { "sourceSystem", "BPMNEW" },
                    { "serviceName", "S_XXX_ISMART_VHanaPurchasePriceForecastToBpm_S" }
                };

                // 拼接dynamic模型
                var sendData = new
                {
                    data = new
                    {
                        table = "BI_DM.dbo.V_HANA_PURCHASE_PRICE_FORECAST_TO_BPM",
                        alias = "V",
                        pk = "MONTH_ID",
                        offset = 1,
                        size = 999999,
                        select = "*",
                        condition = ""
                    }
                };
                VHanaPurchasePriceForecastToBpmOuput res = new VHanaPurchasePriceForecastToBpmOuput();
                var result = await _nPRestHttp.ESBPostAsync(
                    _configuration["Nippon:ESB:ESBNewSqlServerApi"],
                    JsonConvert.SerializeObject(sendData),
                    headers);

                try
                {
                    res = JsonConvert.DeserializeObject<VHanaPurchasePriceForecastToBpmOuput>(result);
                    var groupList = res.Data.GroupBy(x => (string.IsNullOrEmpty(x.CATEGORY_ID) ? string.Empty : x.CATEGORY_ID) +
                        (string.IsNullOrEmpty(x.SUB_CATEGORY_ID) ? string.Empty : x.SUB_CATEGORY_ID) +
                        (string.IsNullOrEmpty(x.CHILD_CATEGORY_ID) ? string.Empty : x.CHILD_CATEGORY_ID) + x.DEPT + x.CATEGORY_MANAGER_CODE)
                        .OrderBy(x => x.FirstOrDefault()?.rownum ?? 9999).ToList();
                    var datas = new List<VHanaPurchasePriceForecastToBpmData>();
                    foreach (var item in groupList)
                    {
                        if (item.Any())
                        {
                            var groupData = item.OrderBy(x => x.rownum).ToList();
                            var data = groupData.First();
                            data.CATEGORY_RATIO = data.CATEGORY_RATIO.HasValue ? Math.Round(data.CATEGORY_RATIO.Value * 100, 2) : data.CATEGORY_RATIO;
                            data.TOTAL_RATIO = data.TOTAL_RATIO.HasValue ? Math.Round(data.TOTAL_RATIO.Value * 100, 2) : data.TOTAL_RATIO;
                            data.pre_num3 = data.RATIO.HasValue ? Math.Round(data.RATIO.Value * 100, 2) : data.RATIO;
                            data.pre_num2 = groupData.Count() >= 2 ?
                                (groupData[1].RATIO.HasValue ? Math.Round(groupData[1].RATIO.Value * 100, 2) : groupData[1].RATIO) : null;
                            data.pre_num1 = groupData.Count() >= 3 ?
                                (groupData[2].RATIO.HasValue ? Math.Round(groupData[2].RATIO.Value * 100, 2) : groupData[2].RATIO) : null;
                            datas.Add(data);
                        }
                    }

                    //if (!string.IsNullOrEmpty(dept) && dept.ToUpper() != "ALL")
                    //{
                    //    datas = datas.Where(x => x.DEPT.Equals(dept)).ToList();
                    //}

                    var dataList = datas.Where(x => x.DEPT.ToUpper().Equals("IU"))
                        .OrderBy(x => x.CATEGORY_ID).ThenBy(x => x.SUB_CATEGORY_ID).ThenBy(x => x.CHILD_CATEGORY_ID).ToList();
                    var tuDatas = datas.Where(x => x.DEPT.ToUpper().Equals("TU"))
                        .OrderBy(x => x.CATEGORY_ID).ThenBy(x => x.SUB_CATEGORY_ID).ThenBy(x => x.CHILD_CATEGORY_ID).ToList();
                    dataList.AddRange(tuDatas);
                    res.Data = dataList;
                }
                catch (Exception e)
                {
                    _logger.Error("从ESB获取行项目数据报错:" + result);
                }

                return res;
            }
            catch (Exception e)
            {
                _logger.Error("批量获取行项目信息报错" + e.Message.ToString());
                throw e;
            }
        }

        /// <summary>
        /// 发送未提交提醒邮件
        /// </summary>
        /// <param name="esbData"></param>
        /// <param name="email"></param>
        /// <param name="ccEmails"></param>
        /// <returns></returns>
        private async Task SendUnSubmitEmail(List<VHanaPurchasePriceForecastToBpmData> esbData, string email, string startInstanceLink, string ccEmails = "")
        {
            var topic = "您好，BPM平台仍未收到您负责品相的原材料未来价格预测数据，请尽快处理！";
            var replaceContent = "";
            var content = "";
            var templateName = "YCLWLJGZSYCUnSubmit.cshtml";

            esbData.ForEach(x =>
            {
                replaceContent += @$"<tr>
                                    <th>{x.DEPT}</th>   
                                    <th>{x.CATEGORY}</th>
                                    <th>{x.SUB_CATEGORY}</th> 
                                    <th>{x.CHILD_CATEGORY}</th>                      
                                    </tr>";
            });
            if (!string.IsNullOrEmpty(templateName))
            {
                content = _emailAPPService.GenerateEmailTemplate(templateName);
                content = !string.IsNullOrEmpty(startInstanceLink) ?
                    content.Replace("{StartInstanceLink}", startInstanceLink) : string.Empty;
                content = !string.IsNullOrEmpty(replaceContent) ?
                    content.Replace("{content}", replaceContent) : string.Empty;
            }
            if (!string.IsNullOrEmpty(content) && !string.IsNullOrEmpty(email))
            {
                await _capBus.PublishAsync("bpmnew.EmailAppService.SendEmailAsync",
                  new SendEmailSimpleDto()
                  {
                      To = GetToEmails(email),
                      Subject = topic,
                      Body = content,
                      CC = ccEmails
                  });
            }
        }

        /// <summary>
        /// 获取已经提交或通过的数据，并去重
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        private async Task<List<YCLWLJGZSYCChildDto>> GetSubmitedData(DateTime startTime, DateTime endTime)
        {
            var query = await _db.Queryable<YCLWLJGZSYCHeader, YCLWLJGZSYCChildData, BusinessProcessesNew>(
                (a, x, b) => new JoinQueryInfos(
                    JoinType.Inner, a.InstanceNumber.Equals(x.InstanceNumber),
                    JoinType.Inner, x.InstanceNumber.Equals(b.InstanceId)))
                .Where((a, x, b) => x.AddTime <= endTime && x.AddTime >= startTime && x.RATIO.HasValue && 
                (b.ProcessStatusCode.Equals("approved") || b.ProcessStatusCode.Equals("processing")))
                .Select((a, x, b) => new YCLWLJGZSYCChildDto
                {
                    MONTH_ID = x.MONTH_ID,
                    DEPT = x.DEPT,
                    CATEGORY_ID = x.CATEGORY_ID,
                    CATEGORY = x.CATEGORY,
                    SUB_CATEGORY_ID = x.SUB_CATEGORY_ID,
                    SUB_CATEGORY = x.SUB_CATEGORY,
                    CHILD_CATEGORY_ID = x.CHILD_CATEGORY_ID ?? string.Empty,
                    CHILD_CATEGORY = x.CHILD_CATEGORY ?? string.Empty,
                    RATIO = x.RATIO,
                    AddTime = x.AddTime,
                    MaintenanceType = a.MaintenanceType,
                    CATEGORY_MANAGER_CODE = x.CATEGORY_MANAGER_CODE,
                    InstanceNumber = x.InstanceNumber,
                }).ToListAsync();
            return query;
        }

        /// <summary>
        /// 获取待审批数据
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        private async Task<List<YCLWLJGZSYCHeader>> GetProcessingData(DateTime startTime, DateTime endTime)
        {
            var query = await _db.Queryable<YCLWLJGZSYCHeader, BusinessProcessesNew>(
                    (a, b) => new JoinQueryInfos(
                    JoinType.Inner, a.InstanceNumber.Equals(b.InstanceId)))
                    .Where((a, b) => a.AddTime <= endTime && a.AddTime >= startTime && 
                    b.ProcessStatusCode.Equals("processing"))
                    .Select(a => a).ToListAsync();

            return query;
        }

        /// <summary>
        /// 发送未审批提醒邮件
        /// </summary>
        /// <param name="data"></param>
        /// <param name="email"></param>
        /// <param name="ccEmails"></param>
        /// <returns></returns>
        private async Task SendProcessingEmail(List<YCLProcessingSendEamilDto> data, string email, string ccEmails = "")
        {
            var topic = "您好，BPM平台有待您审批的原材料未来价格预测数据，请尽快处理！";
            var replaceContent = "";
            var content = "";
            var templateName = "YCLWLJGZSYCProcessing.cshtml";

            data.ForEach(x =>
            {
                replaceContent += @$"<tr>
                                    <th>{x.InstanceNumber}</th>   
                                    <th>{x.StartUser}</th>
                                    <th><a href='{x.ApprovelUrl}' target='_blank'>{x.ApprovelUrl}</a></th>               
                                    </tr>";
            });
            if (!string.IsNullOrEmpty(templateName))
            {
                content = _emailAPPService.GenerateEmailTemplate(templateName);
                content = !string.IsNullOrEmpty(replaceContent) ?
                    content.Replace("{content}", replaceContent) : string.Empty;
            }
            if (!string.IsNullOrEmpty(content) && !string.IsNullOrEmpty(email))
            {
                await _capBus.PublishAsync("bpmnew.EmailAppService.SendEmailAsync",
                  new SendEmailSimpleDto()
                  {
                      To = GetToEmails(email),
                      Subject = topic,
                      Body = content,
                      CC = ccEmails
                  });
            }
        }

        /// <summary>
        /// 根据流程编号获取流程详情
        /// </summary>
        /// <param name="instanceNumbers">流程号</param>
        /// <returns></returns>
        public async Task<List<InstanceDtoForYCL>> GetInstanceDetail(List<string> instanceNumbers)
        {
            var dbquetty = _dbEngine.Queryable<Instances, InstanceStates, Tasks>((i, ins, t) =>
            new JoinQueryInfos(
                JoinType.Inner,
                i.InstanceId == ins.InstanceId,
                JoinType.Left,
                t.InstanceNumber == i.Number && t.Status == 1))
                  .Where((i, ins, t) => instanceNumbers.Contains(i.Number) && ins.Status == 1)
                  .Select((i, ins, t) =>
                  new InstanceDtoForYCL
                  {
                      InstanceNumber = i.Number,
                      Topic = i.Topic,
                      StatusName = ins.StatusName,
                      CurrentApprover = t.UserId,
                      TaskId = t.TaskId
                  });
            var result = await dbquetty.ToListAsync();
            return result;
        }

        /// <summary>
        /// 获取流程发起页面url
        /// </summary>
        /// <returns></returns>
        private async Task<string> GetPCStartUrl()
        {
            var process = await _dbProcess.Queryable<Processes>().FirstAsync(x => x.BTID.Equals("YCLWLJGZSYC") && x.InUse == 1);
            if (process == null)
            {
                return "";
            }

            return string.Format(_configuration["BPM:PCStartUrl"], process.ProcessId);
        }
    }
}
